package main

import (
	"context"
	"fmt"
	proto "jaegerDemo/pb"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/metadata"
)

func main() {
	r := gin.Default()
	// 加中间件
	r.Use(jaegerMid)

	r.GET("/ping", func(ctx *gin.Context) {
		client, gctx := getGrpcClient(ctx)
		out, err := client.SayHello(gctx, &proto.HelloRequest{Name: "wjf"})
		if err != nil {
			fmt.Println(err.Error())
			ctx.JSON(400, err)
		}
		outHi, err := client.SayHi(gctx, &proto.HiRequest{Name: "jjj"})
		if err != nil {
			fmt.Println(err.Error())
			ctx.JSON(400, outHi)
		}
		endOut := make(map[string]interface{})
		endOut["out_msg"] = out.Message
		endOut["outHi_msg"] = outHi.Message
		ctx.JSONP(200, endOut)
	})
	r.Run("127.0.0.1:8088")
}

// jaeger gin中间件
func jaegerMid(ctx *gin.Context) {
	cfg := jaegercfg.Configuration{
		ServiceName: "gin_client", // 对其发起请求的的调用链，叫什么服务
		Sampler: &jaegercfg.SamplerConfig{
			Type:  jaeger.SamplerTypeConst,
			Param: 1,
		},
		Reporter: &jaegercfg.ReporterConfig{
			LogSpans:          true,
			CollectorEndpoint: "http://localhost:14268/api/traces",
		},
	}
	tracer, close, err := cfg.NewTracer() // 根据配置创建一个tracer
	if err != nil {
		panic(err)
	}
	defer close.Close()
	// 设置为全局tracer,方便调用。go里面，每一个请求都有一个独立的协程去运行，所以这里设置为全局tracer不影响其他请求
	opentracing.SetGlobalTracer(tracer)
	span := tracer.StartSpan(ctx.Request.RequestURI)                                                // 第一个span
	ctx.Request = ctx.Request.WithContext(opentracing.ContextWithSpan(ctx.Request.Context(), span)) // 在gin里往下游传递ctx
	defer func() {
		if ctx.Writer.Status() != 200 {
			ext.Error.Set(span, true)
		}
		span.LogKV("status", ctx.Writer.Status())
		span.Finish()
	}()
	ctx.Next()
}

// 获取请求grpc服务的链接
func getGrpcClient(ctx *gin.Context) (proto.GreeterClient, context.Context) {
	conn, err := grpc.Dial("127.0.0.1:8089", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		panic(err)
	}
	// 将span注入到grpc的元数据，作为gin\grpc之间跨进程的载体
	span := opentracing.SpanFromContext(ctx.Request.Context())
	md := metadata.Pairs()
	opentracing.GlobalTracer().Inject(
		span.Context(),
		opentracing.HTTPHeaders,
		opentracing.HTTPHeadersCarrier(md),
	)
	md = alterMdKey(md) // 坑1：jaeger传递ctx过去带的key是Uber-Trace-Id,转为小写后即可，tracer.Extract可识别
	c := metadata.NewOutgoingContext(context.Background(), md)
	client := proto.NewGreeterClient(conn)
	return client, c
}

// grpc中不允许有大写字母。
func alterMdKey(md metadata.MD) metadata.MD {
	for k, v := range md {
		delete(md, k)
		md[strings.ToLower(k)] = v
	}
	return md
}
